package net.shambolica.ember.jinterface.wrappers.objects;

import com.ericsson.otp.erlang.OtpErlangObject;

import java.util.*;

/**
 * A set of patterns for matching against.
 */
public class EOMatchSpec {

    private final Object[]    keys;
    private final EOPattern[] patterns;


    /**
     * This constructor takes pairs of key-pattern, e.g.
     * &lt;br&gt;    new EOMatchSpec("one", patom("hello"),
     * &lt;bt&gt;                    "two", plist(pvar("X")),
     * &lt;bt&gt;                    123123, ptuple(atom("xx"),pvar("N")));
     * @param keysAndPatterns   A flat series of key-patterns.
     */
    public EOMatchSpec(Object ... keysAndPatterns) {

        int len = keysAndPatterns.length;

        if (len == 0) {
            throw new IllegalArgumentException("invalid matchspec - empty set");
        }
        if (keysAndPatterns.length % 2 != 0) {
            throw new IllegalArgumentException("invalid matchspec - apparent key without pattern");
        }

        int npatterns = len / 2;
        keys = new Object[npatterns];
        patterns = new EOPattern[npatterns];

        for (int i = 0 ; i < npatterns ; i++) {
            Object key = keysAndPatterns[i];
            Object pattern = keysAndPatterns[i+1];

            if (key == null) {
                throw new IllegalArgumentException("invalid matchspec - null key at index "+i);
            }
            if (pattern == null) {
                throw new IllegalArgumentException("invalid matchspec - null value at index "+i);
            }
            if (pattern instanceof OtpErlangObject) {
                pattern = EOP.pterm((OtpErlangObject) pattern);
            }
            if (! (pattern instanceof EOPattern)) {
                throw new IllegalArgumentException("invalid matchspec - value can only be ErlangOtpObject or EOPattern at index "+i);
            }

            keys[i] = key;
            patterns[i] = (EOPattern)pattern;
        }
    }


    public EOMatch match(final OtpErlangObject term) {
        if (term == null) {
            return noMatch;
        }

        for (int i = 0 ; i < keys.length ; i++) {
            final Map<String,OtpErlangObject> match = patterns[i].match(term, null);
            if (match != null) {
                final Object key = keys[i];
                return new EOMatch() {
                    public boolean isMatched() {
                        return true;
                    }

                    public Object getMatchKey() {
                        return key;
                    }

                    public Map<String, OtpErlangObject> getMatchVars() {
                        return match;
                    }

                    public OtpErlangObject getObject() {
                        return term;
                    }
                };
            }
        }
        return noMatch;
    }


    public static final EOMatch noMatch = new EOMatch() {



        public boolean isMatched() {
            return false;
        }

        public Object getMatchKey() {
            return null;
        }

        public Map<String, OtpErlangObject> getMatchVars() {
            return new HashMap<String, OtpErlangObject>(0);
        }

        public OtpErlangObject getObject() {
            return null;
        }
        
    };

}
